﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace BGSpaceShooter
{
    public class Camera
    {

        public StarFighter FollowedObject { get; set; }
        public Vector3 CameraPosition { get; set; }
        private Vector3 CameraUp { get; set; }

        private Quaternion CameraRotation { get; set; }

        private Vector3 Gravitometer = new Vector3();
        private Boolean GravitometerActive { get; set; }

        private Matrix freeViewMatrix = Matrix.Identity;
        private Matrix rollMatrix = Matrix.Identity;
        private float rollVal = 0;

        private float xengine = 0;
        private float yengine = 0;
        private float zengine = 0;

        private const float ENGINE_DELTA = 0.1f;
        private const float ENGINE_LIMIT = 5;

        public Camera()
        {
            CameraPosition = new Vector3(0);
            CameraUp = new Vector3(0);
            CameraRotation = Quaternion.Identity;
        }

        /// <summary>
        /// Zwraca macierz widoku
        /// </summary>
        public Matrix CameraMatrix
        {

            get
            {
                return Matrix.CreateLookAt(CameraPosition, FollowedObject.Position, CameraUp);
            }
        }

        public void UpdateCamera()
        {
            lock (this)
            {
                CameraRotation = Quaternion.CreateFromRotationMatrix(FollowedObject.RotationMatrix * freeViewMatrix);

                CameraPosition = new Vector3(0, 3.5f, 20);
                CameraPosition += new Vector3(xengine, yengine, zengine);
                CameraPosition = Vector3.Transform(CameraPosition, CameraRotation);

                if (GravitometerActive)
                {
                    CameraPosition += Gravitometer;
                }

                CameraPosition += FollowedObject.Position;

             //   CameraRotation = Quaternion.CreateFromRotationMatrix(FollowedObject.RotationMatrix * rollMatrix * freeViewMatrix);

                CameraUp = new Vector3(0, 1, 0);
                CameraUp = Vector3.Transform(CameraUp, CameraRotation);


            }
        }



        public void UpdateEngineRotation(Vector3 inputVelocity)
        {

            Vector3 velocity = inputVelocity;
            Console.WriteLine("===");
            Console.WriteLine(inputVelocity.ToString());
            Console.WriteLine(velocity.ToString());
            Console.WriteLine(CameraPosition.ToString());
            xengine =- velocity.X*200;
            yengine =- velocity.Y*200;
            zengine =- velocity.Z*200;

            xengine = constraintWithSign(xengine);
            yengine = constraintWithSign(yengine);
            zengine = constraintWithSign(zengine);
            

        }

        private float constraintWithSign(float val)
        {
            return (Math.Abs(val) < 2 ? val : Math.Sign(val) * 2);
        }

        public void UpdateEngines(List<Keys> keys)
        {

            if (!keys.Contains(Keys.A) && !keys.Contains(Keys.D))
            {
                yengine = getNewAxisValue(keys, Keys.Down, Keys.Up, yengine);
                xengine = getNewAxisValue(keys, Keys.Right, Keys.Left, xengine);
            }
            else
            {
                yengine = getRollEngineValue(keys, yengine);
                xengine = getNewAxisValue(keys, Keys.D, Keys.A, xengine);
            }
            // calculateRollMatrix(keys);
            zengine = getMainEngineValue(keys,zengine);




        }

        private float getMainEngineValue(List<Keys> keys, float currentValue)
        {
            if (keys.Contains(Keys.W) && currentValue <= ENGINE_LIMIT/2)
            {
                currentValue += ENGINE_DELTA;
            }
            else if (currentValue > 0 && !keys.Contains(Keys.W))
            {
                currentValue -= ENGINE_DELTA * 2;
                if (currentValue < 0)
                {
                    currentValue = 0;
                }
            }



            return currentValue;
        }

        public void calculateRollMatrix(List<Keys> keys)
        {
            Vector3 CameraFwd = Vector3.Transform(new Vector3(0, 0, 1), Quaternion.CreateFromRotationMatrix(FollowedObject.RotationMatrix));
            float currentValue = rollVal;
            const float rollCoeff = 0.005f;
            if (keys.Contains(Keys.A)  && currentValue <= ENGINE_LIMIT)
            {
                currentValue += ENGINE_DELTA;
             
                rollMatrix *= Matrix.CreateFromAxisAngle(CameraFwd, -rollCoeff);
            }
            else if (currentValue > 0 && !keys.Contains(Keys.D))
            {
                currentValue -= ENGINE_DELTA;
                rollMatrix *= Matrix.CreateFromAxisAngle(CameraFwd, rollCoeff);
                if (currentValue < 0)
                {
                    currentValue = 0;
                    rollMatrix = Matrix.Identity;
                    
                }
            }


            if (keys.Contains(Keys.D) && currentValue >= -ENGINE_LIMIT)
            {
                currentValue -= ENGINE_DELTA;
                rollMatrix *= Matrix.CreateFromAxisAngle(CameraFwd, rollCoeff);
            }
            else if (currentValue < 0 && !keys.Contains(Keys.A))
            {
                currentValue += ENGINE_DELTA;
                rollMatrix *= Matrix.CreateFromAxisAngle(CameraFwd, -rollCoeff);
                if (currentValue > 0)
                {
                    currentValue = 0;
                   rollMatrix = Matrix.Identity;
                }
            }
            rollVal = currentValue;
        }

        private float getRollEngineValue(List<Keys> keys, float currentValue)
        {

            if ((keys.Contains(Keys.A) || keys.Contains(Keys.D)) && currentValue >=- ENGINE_LIMIT)
            {
                currentValue -= ENGINE_DELTA;
            }
            else if (currentValue < 0 && !(keys.Contains(Keys.A) || keys.Contains(Keys.D)))
            {
                currentValue += ENGINE_DELTA * 2;
                if (currentValue > 0)
                {
                    currentValue = 0;
                }
            }



            return currentValue;
        }

        private float getNewAxisValue(List<Keys> engines, Keys engineName, Keys oppositeEngineName, float currentValue)
        {

            if (engines.Contains(engineName) && currentValue <= ENGINE_LIMIT)
            {
                currentValue += ENGINE_DELTA;
            }
            else if (currentValue > 0 && !engines.Contains(engineName))
            {
                currentValue -= ENGINE_DELTA * 2;
                if (currentValue < 0)
                {
                    currentValue = 0;
                }
            }


            if (engines.Contains(oppositeEngineName) && currentValue >=-ENGINE_LIMIT)
            {
                currentValue -= ENGINE_DELTA;
            }
            else if (currentValue < 0 && !engines.Contains(oppositeEngineName))
            {
                currentValue += ENGINE_DELTA * 2;
                if(currentValue>0)
                {
                    currentValue = 0;
                }
            }

            return currentValue;

        }
        


        public void UpdateGravityInfo(GamePad gamePad)
        {
            Vector3 calcVect = new Vector3();
            double delta = 0.5;

            Vector3 gravVector = gamePad.GyroscopeValues;
            Boolean isActive = gamePad.GyroscopeActive;



            if (Math.Abs(gravVector.Y) < delta)
                calcVect.X = 0;
            else calcVect.X = Math.Sign(gravVector.Y);

            if (Math.Abs(gravVector.X) < delta)
                calcVect.Y = 0;
            else calcVect.Y = Math.Sign(gravVector.X);

            /* if (Math.Abs(gravVector.Z) -0.9 < delta)
                 calcVect.X = 0;
             else calcVect.X = Math.Sign(gravVector.Z - 0.9);
             */

            Quaternion newRotation = Quaternion.CreateFromRotationMatrix(FollowedObject.RotationMatrix * freeViewMatrix);
            calcVect = Vector3.Transform(calcVect, newRotation);

            /*  float max = 10.0f;
              if (Math.Abs(Gravitometer.X) > max)
              {
                  Gravitometer.X = max * Math.Sign(Gravitometer.X);
              }

              if (Math.Abs(Gravitometer.Y) > max)
              {
                  Gravitometer.Y = max * Math.Sign(Gravitometer.Y);
              }

               if (Math.Abs(Gravitometer.Z) > max)
               {
                   Gravitometer.Z = max * Math.Sign(Gravitometer.Z);
               }
              */
            if (isActive && !GravitometerActive)
            {
                Gravitometer = Vector3.Zero;
                freeViewMatrix = Matrix.Identity;
            }

            if(!isActive)
            {
                freeViewMatrix = Matrix.Identity;
            }

            Gravitometer += calcVect / 4;

            Vector3 LocalCameraUp = new Vector3(0, 1, 0);
            LocalCameraUp = Vector3.Transform(CameraUp, Quaternion.CreateFromRotationMatrix(FollowedObject.RotationMatrix));
            if (gamePad.LeftButton)
            {
                freeViewMatrix *= Matrix.CreateFromAxisAngle(LocalCameraUp, 0.01f);
            }
            if (gamePad.RightButton)
            {
                freeViewMatrix *= Matrix.CreateFromAxisAngle(LocalCameraUp, -0.01f);
            }
            //Gravitometer.Normalize();
            GravitometerActive = isActive;
        }
    }
}
